#pragma once

#include <iostream>
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>

const size_t num = 5;

template<class T>
class block_queue
{
public:
    block_queue(int size = num) : _cap(size)
    {}

    ~block_queue()
    {}

    void push(const T& data)
    {
        std::unique_lock<std::mutex> lock(_mtx);
        _pdr_cv.wait(lock, [&](){ return _q.size() < _cap; });

        _q.push(data);
        _csr_cv.notify_one();
    }

    void pop(T* data)
    {
        std::unique_lock<std::mutex> lock(_mtx);
        _csr_cv.wait(lock, [&](){ return _q.size(); });

        *data = _q.front();
        _q.pop();

        _pdr_cv.notify_one();
    }

private:
    std::queue<T> _q;
    size_t _cap;

    std::mutex _mtx;
    std::condition_variable _csr_cv; // consumer care it
    std::condition_variable _pdr_cv; // producer care it
};

